﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JGSY.SerialCommunication.UpperComputer.Core
{
    public sealed class ModbusCrcCalculator
    {
        private const ushort InitialValue = 0xFFFF;
        private const ushort Polynomial = 0xA001;
        private static readonly ushort[] CrcTable = InitializeCrcTable();

        private static ushort[] InitializeCrcTable()
        {
            var table = new ushort[256];
            for (ushort i = 0; i < table.Length; i++)
            {
                ushort crc = i;
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                        crc = (ushort)((crc >> 1) ^ Polynomial);
                    else
                        crc >>= 1;
                }
                table[i] = crc;
            }
            return table;
        }

        public static ushort Calculate(byte[] data)
        {
            if (data == null || data.Length == 0)
                throw new ArgumentNullException(nameof(data));

            ushort crc = InitialValue;
            foreach (byte b in data)
            {
                crc = (ushort)((crc >> 8) ^ CrcTable[(crc ^ b) & 0xFF]);
            }
            return crc;
        }

        public static bool Verify(byte[] data, ushort expectedCrc)
        {
            if (data == null || data.Length < 2)
                return false;

            ushort calculatedCrc = Calculate(data);
            return calculatedCrc == expectedCrc;
        }

        /// <summary>
        /// 计算Modbus RTU CRC校验  大端序
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static ushort CalculateCrc(byte[] data)
        {
            ushort crc = 0xFFFF;

            foreach (byte b in data)
            {
                crc ^= b;

                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }

            return crc;
        }
    }
}
